unit uHero;

interface

uses
  Types,

  uPathFind;


const
  WalkTickTrigger = 3;

type
  THero = class
  private
    FPos: TPoint;
    FTicks: Cardinal;
    FPath: TPath;
    FPathMap: TPathMap;
    FPathMapStart: TPoint;
    FCurPathCellIdx: Integer;
    FIsMoving: Boolean;
    FDest: TPoint;
    function GetPath(Index: Integer): TPoint;
    procedure SetCurPathCellIdx(const Value: Integer);
    procedure SetPath(const Value: TPath);
    function GetPathLen: Integer;
    function GetIsValidPath: Boolean;
    function GetIsFinished: Boolean;
    procedure SetDest(const Value: TPoint);
  protected
    procedure Step();
    function MovingCost (X, Y, Direction: Integer): Integer;
    property ThePath: TPath read FPath write SetPath;
    property CurPathCellIdx: Integer read FCurPathCellIdx write SetCurPathCellIdx;
    property Dest: TPoint read FDest write SetDest;
  public
    constructor Create(APos: TPoint); overload;
    constructor Create(AX, AY: Byte); overload;
    destructor Destroy;
    property Pos: TPoint read FPos write FPos;
    procedure Update();
    function SearchPath(ADest: TPoint): Boolean;
    procedure ProceedPath(ADest: TPoint);
    procedure StartMove;
    property IsMoving: Boolean read FIsMoving;
    property Path[Index: Integer]: TPoint read GetPath;
    property PathLen: Integer read GetPathLen;
    property IsValidPath: Boolean read GetIsValidPath;
    property IsFinished: Boolean read GetIsFinished;
  end;

var
  Hero: THero;

implementation

uses
  uUtils, uMap, uSceneGame;

{ THero }

constructor THero.Create(APos: TPoint);
begin
  Pos := APos;
end;

constructor THero.Create(AX, AY: Byte);
begin
  Create(Point(AX, AY));
end;

destructor THero.Destroy;
begin

end;

function THero.GetIsFinished: Boolean;
begin
  Result := CurPathCellIdx = PathLen - 1;
end;

function THero.GetIsValidPath: Boolean;
begin
  Result := (PathLen > 0);
end;

function THero.GetPath(Index: Integer): TPoint;
begin
  Result := Point(-1, -1);
  if not IsValidPath or ((Index >= 0) and (Index > PathLen))
    or ((Index < 0) and (PathLen + Index {negative value} < 0))
  then
    Exit;
  if Index < 0 then
    Result := FPath[PathLen + Index]
  else
    Result := FPath[Index];
end;

function THero.GetPathLen: Integer;
begin
  Result := Length(ThePath);
end;

function THero.MovingCost(X, Y, Direction: Integer): Integer;
var
  I : Integer;
  Pt: TPoint;
begin
  Result := -1;
  Pt := Point(X, Y);
  if ((Map.FMap[Y, X, lrObj] >= 0) and not EqualPoints(pt, Dest)) or
    ((SceneGame.IsMapCreature(X, Y) >= 0) and not EqualPoints(pt, Dest, AggrZone))
  then
     Exit;
  // cost of terrain
  if (Map.FMap[Y][X][lrTerrain] in GroundSet) then
    Result := 2
  else
    Result := 5;
  if (Direction and 1 > 0) then // 150% cost for diagonal
    Inc(Result, Result div 2);
end;

procedure THero.ProceedPath(ADest: TPoint);
begin
  if IsValidPath and not IsMoving then
    if EqualPoints(Dest, ADest) then
      StartMove
    else
      SearchPath(ADest);
  if not IsValidPath or IsFinished then
    SearchPath(ADest);
end;

function THero.SearchPath(ADest: TPoint): Boolean;
begin
  Dest := ADest;
  if not EqualPoints(FPathMapStart, FPos) or (Length(FPathMap) = 0) then
    FPathMap := MakePathMap(Map.Width, Map.Height, FPos.X, FPos.Y, MovingCost);
  ThePath := FindPathOnMap(FPathMap, ADest.X, ADest.Y);
end;

procedure THero.SetCurPathCellIdx(const Value: Integer);
begin
  FCurPathCellIdx := Value;
  Map.SetPath(Path[CurPathCellIdx], lrNone);
end;

procedure THero.SetDest(const Value: TPoint);
begin
  FDest := Value;
end;

procedure THero.SetPath(const Value: TPath);
var
  I: Integer;
begin
  for I := CurPathCellIdx to PathLen - 1 do
    Map.SetPath(Path[i], lrNone);
  FPath := Value;
  if not IsValidPath then
    CurPathCellIdx := -1
  else
    CurPathCellIdx := 0;
  for I := CurPathCellIdx + 1 to PathLen - 1 do
    Map.SetPath(Path[i], 1);
end;

procedure THero.StartMove;
begin
  FIsMoving := True;
end;

procedure THero.Step();
begin
  if not IsValidPath or not IsMoving then
    Exit;
  if IsFinished then
  begin
    FIsMoving := False;
    Exit;
  end;
  CurPathCellIdx := CurPathCellIdx + 1;
  Pos := Path[CurPathCellIdx];
end;

procedure THero.Update();
begin
  Inc(FTicks);
  if FTicks mod WalkTickTrigger = 0 then
    Step();
end;

initialization
  Hero := THero.Create(0, 0);

finalization
  Hero.Free;

end.
